﻿Imports System.Reflection
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized

''' <summary>
''' アイテムクラスアーカイブ
''' </summary>
''' <remarks>
''' </remarks>
Public Class ItemEntityArchive
    Implements IEntityArchive, IDirtyObjectConverter

    Private _classType As Type
    ''' <summary>
    ''' クラスタイプ
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property ClassType As Type
        Get
            Return _classType
        End Get
        Friend Set(ByVal value As Type)
            _classType = value
        End Set
    End Property

    Private _entitylKey As String
    ''' <summary>
    ''' エンティティキー
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property EntityKey() As String
        Get
            Return _entitylKey
        End Get
        Friend Set(ByVal value As String)
            _entitylKey = value
        End Set
    End Property

    ''' <summary>
    ''' ユニーク文字列
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Key As String
        Get
            Return _entitylKey
        End Get
    End Property

    Private _baseArchive As ItemEntityArchive
    ''' <summary>
    ''' 基底クラスアーカイブ
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property BaseArchive() As ItemEntityArchive
        Get
            Return _baseArchive
        End Get
        Friend Set(ByVal value As ItemEntityArchive)
            _baseArchive = value
        End Set
    End Property

    Private _itemArchives As Dictionary(Of String, LiteralPropertyArchive)
    ''' <summary>
    ''' アイテムアーカイブの辞書
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property ItemArchives As Dictionary(Of String, LiteralPropertyArchive)
        Get
            If _itemArchives Is Nothing Then _itemArchives = New Dictionary(Of String, LiteralPropertyArchive)
            Return _itemArchives
        End Get
    End Property

    Private _colletionArchives As Dictionary(Of String, CollectionPropertyArchive)
    ''' <summary>
    ''' コレクションアーカイブの辞書
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property CollectionArchives As Dictionary(Of String, CollectionPropertyArchive)
        Get
            If _colletionArchives Is Nothing Then _colletionArchives = New Dictionary(Of String, CollectionPropertyArchive)
            Return _colletionArchives
        End Get
    End Property

    ''' <summary>
    ''' ダーティなアイテムプロパティを取得します
    ''' </summary>
    ''' <param name="instance"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetDirtyItemProperties(instance As Object) As IEnumerable(Of LiteralPropertyArchive)
        Return (From el In Me.ItemArchives.Select(Function(kv) kv.Value) Where el.IsDirty(instance))
    End Function

    ''' <summary>
    ''' ダーティかどうかを示します
    ''' </summary>
    ''' <param name="instance">検査対象インスタンス</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function IsDirty(instance As Object) As Boolean
        '基底チェック
        If Me.BaseArchive IsNot Nothing AndAlso Me.BaseArchive.IsDirty(instance) Then Return True

        'アイテムチェック
        If Me.GetDirtyItemProperties(instance).Any Then Return True

        'コレクションチェック
        If (From el In Me.CollectionArchives.Select(Function(kv) kv.Value) Where el.IsDirty(instance)).Any Then Return True

        Return False
    End Function

    ''' <summary>
    ''' ダーティオブジェクトに変換します
    ''' </summary>
    ''' <param name="instance"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function ToDirtyObjects(instance As Object) As ICollection(Of DirtyObject) Implements IDirtyObjectConverter.ToDirtyObjects
        Dim results As New List(Of DirtyObject)

        '基底へカスケード
        If Me.BaseArchive IsNot Nothing Then results.AddRange(Me.BaseArchive.ToDirtyObjects(instance))

        '更新（更新されていない場合は対象に含めない）
        Dim c = Me.ToUpdateDirtyOrNothing(instance)
        If c IsNot Nothing Then results.Add(c)

        'コレクションへカスケード
        For Each item In Me.CollectionArchives.Select(Function(kv) kv.Value)
            Dim ret = item.ToDirtyObjects(instance)
            results.AddRange(ret)
        Next

        Return results
    End Function

    ''' <summary>
    ''' 更新ダーティに変換します
    ''' </summary>
    ''' <param name="instance"></param>
    ''' <returns></returns>
    ''' <remarks>更新されていない場合はNothingが返されます</remarks>
    Private Function ToUpdateDirtyOrNothing(instance As Object) As UpdateDirtyObject
        'ダーティな値を取得
        Dim q = Me.GetDirtyItemProperties(instance)
        If q.Any = False Then Return Nothing

        Dim c As New UpdateDirtyObject(Me, instance, q.ToList)
        Return c
    End Function

    ''' <summary>
    ''' 追加ダーティに変換します
    ''' </summary>
    ''' <param name="instance"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Friend Function ToAddDirtyObjects(instance As Object) As ICollection(Of DirtyObject) Implements IDirtyObjectConverter.ToAddDirtyObjects
        Dim results As New List(Of DirtyObject)

        '基底へカスケード
        If Me.BaseArchive IsNot Nothing Then results.AddRange(Me.BaseArchive.ToAddDirtyObjects(instance))

        '自身を追加ダーティに変換
        results.Add(New AddDirtyObject(instance, Me.ClassType))

        'コレクションへカスケード
        For Each item In Me.CollectionArchives.Select(Function(kv) kv.Value)
            Dim ret = item.ToAddDirtyObjects(instance)
            results.AddRange(ret)
        Next

        Return results
    End Function

    ''' <summary>
    ''' 削除ダーティに変換します
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Friend Function ToDeleteDirtyObjects() As ICollection(Of DirtyObject) Implements IDirtyObjectConverter.ToDeleteDirtyObjects
        Dim results As New List(Of DirtyObject)

        '基底へカスケード
        If Me.BaseArchive IsNot Nothing Then results.AddRange(Me.BaseArchive.ToDeleteDirtyObjects)

        '自身を削除ダーティに変換
        results.Add(New DeleteDirtyObject(Me))

        'コレクションへカスケード
        For Each item In Me.CollectionArchives.Select(Function(kv) kv.Value)
            Dim ret = item.ToDeleteDirtyObjects()
            results.AddRange(ret)
        Next

        Return results
    End Function

    ''' <summary>
    ''' アイテムエンティティアーカイブを取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Archives As System.Collections.Generic.IEnumerable(Of ItemEntityArchive) Implements IEntityArchive.Archives
        Get
            '自身を配列にして返します
            Return {Me}
        End Get
    End Property
End Class